﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using Dycox.ComponentModel;
using System.ComponentModel;

namespace Dycox.Workflows
{
    [DataContract]
    internal class ProcessProperties : NotifyPropertyChanged
    {
        private ProcessProperties()
        {

        }

        internal ProcessProperties(string version)
        {
            _version = version;
        }

        [DataMember(Name = "Locks", EmitDefaultValue = false), DefaultValue(null)]
        private ThreadLockData[] _threadLocks;

        public ThreadLockData[] ThreadLocks
        {
            get { return _threadLocks.ToArray(); }
        }
        
        internal ThreadLockData GetThreadLockData(string name)
        {
            ThreadLockData data;

            if (_threadLocks != null) data = _threadLocks.SingleOrDefault(o => o.Name == name);
            else data = null;

            if (data == null)
            {
                data = new ThreadLockData(name);
                SetupThreadLockData(data);
                int length = _threadLocks == null ? 0 : _threadLocks.Length;
                Array.Resize(ref _threadLocks, length + 1);
                _threadLocks[length] = data;
                SendPropertyChanged("ThreadLockData");
            }

            return data;
        }

        private void SetupThreadLockData(ThreadLockData data)
        {
            data.CounterChanged += CounterChanged;
        }

        void CounterChanged(object sender, EventArgs e)
        {
            this.SendPropertyChanged("ThreadLockData");
        }

        [OnDeserialized]
        void OnDeserialized(StreamingContext streaming)
        {
            if (_threadLocks != null)
            {
                foreach (var data in _threadLocks)
                    SetupThreadLockData(data);
            }
        }

        [DataMember(Name = "Ver")]
        private string _version;

        public string Version
        {
            get { return _version; }
        }

        [DataMember(Name = "AbortReason", EmitDefaultValue = false), DefaultValue(null)]
        private string _abortReason;

        public string AbortReason
        {
            get { return _abortReason; }
            set
            {
                if (_abortReason != value)
                {
                    _abortReason = value;
                    SendPropertyChanged("AbortChanged");
                }
            }
        }

        [DataMember(Name = "AbortStep", EmitDefaultValue = false), DefaultValue(null)]
        private string _abortStepId;

        public string AbortStepId
        {
            get { return _abortStepId; }
            set
            {
                if (_abortStepId != value)
                {
                    _abortStepId = value;
                    SendPropertyChanged("AbortStepId");
                }
            }
        }

        [DataMember(Name = "AbortStage", EmitDefaultValue = false), DefaultValue(null)]
        private string _abortStageName;

        public string AbortStageName
        {
            get { return _abortStageName; }
            set
            {
                if (_abortStageName != value)
                {
                    _abortStageName = value;
                    SendPropertyChanged("AbortStageName");
                }
            }
        }

        private ProcessSuspendingReasons _reasons = ProcessSuspendingReasons.None;
        [DataMember(Name = "SuspendingReasons", EmitDefaultValue = false), DefaultValue(typeof(ProcessSuspendingReasons), "None")]
        public ProcessSuspendingReasons SuspendingReasons
        {
            get { return _reasons; }
            set
            {
                if (_reasons != value)
                {
                    _reasons = value;
                    SendPropertyChanged("SuspendingReasons");
                }
            }
        }

        private WorkflowComponentStatus _suspendingStatus;
        [DataMember(EmitDefaultValue = false), DefaultValue(typeof(WorkflowComponentStatus), "Idling")]
        public WorkflowComponentStatus SuspendingStatus
        {
            get { return _suspendingStatus; }
            set
            {
                if (_suspendingStatus != value)
                {
                    _suspendingStatus = value;
                    SendPropertyChanged("SuspendingStatus");
                }
            }
        }

        private ComponentIdentifier _suspendingStep;
        [DataMember(EmitDefaultValue = false), DefaultValue(typeof(ComponentIdentifier), "Empty")]
        public ComponentIdentifier SuspendingStep
        {
            get { return _suspendingStep; }
            set
            {
                if (_suspendingStep != value)
                {
                    _suspendingStep = value;
                    SendPropertyChanged("SuspendingStep");
                }
            }
        }


        public override string ToString()
        {
            return JsonConvert.SerializeObject(this);
        }

        public static ProcessProperties Parse(string text)
        {
            if (string.IsNullOrWhiteSpace(text))
                return null;

            return JsonConvert.DeserializeObject<ProcessProperties>(text);
        }

    }

    [DataContract]
    internal class ThreadLockData
    {
        private ThreadLockData()
        {

        }

        public ThreadLockData(string name)
        {
            _name = name;
        }

        [DataMember]
        private string _name;

        public string Name
        {
            get { return _name; }
        }

        [DataMember]
        private int _counter;

        public int Counter
        {
            get { return _counter; }
        }

        public event EventHandler CounterChanged;

        private void RaiseCounterChanged()
        {
            if (CounterChanged != null)
                CounterChanged(this, EventArgs.Empty);
        }

        public int Increase()
        {
            _counter++;
            RaiseCounterChanged();
            return _counter;
        }

        public int Decrease()
        {
            if (_counter == 0)
                throw new WorkflowException("Thread lock " + this._name + " cannot release any more.");

            _counter--;
            RaiseCounterChanged();
            return _counter;
        }
    }
}
